//
//  OpaqueTypesExample.swift
//  SwiftCode
//
//  Created by yuhus on 2024/1/2.
//

import Foundation

class OpaqueTypesExample: ListViewController {
  override func getItemCount() -> Int {
    data.count
  }
  
  override func getItem(_ indexPath: IndexPath) -> Item {
    data[indexPath.row]
  }
  
  private lazy var data: [Item] = [
    .init(title: "不透明类型解决的问题-1", description: "三角形的字符串", action: drawTriangle),
    .init(title: "不透明类型解决的问题-2", description: "垂直翻转三角形，会暴露用于构造结果的泛型类型", action: flippedTriangle),
    .init(title: "不透明类型解决的问题-3", description: "拼接一个翻转三角形和一个普通三角形", action: joinedShape),
    // 模块内部可能有多种实现方式，而外部使用时，应该与内部各种变换顺序的实现逻辑无关。
    .init(title: "返回不透明类型-1", description: "返回一个梯形", action: testTrapezoid),
    .init(title: "返回不透明类型-2", description: "拼接三角形和三角形的垂直翻转", action: testOpaqueJoinedTriangles),
    
  ]
}

protocol Shape{
  func draw() -> String
}

struct Triangle: Shape {
  var size: Int
  
  func draw() -> String {
    var result: [String] = []
    for length in 1...size {
      result.append(String(repeating: "*", count: length))
    }
    return result.joined(separator: "\n")
  }
}

private func drawTriangle() {
  let smallTriangle = Triangle(size: 3)
  print(smallTriangle.draw())
}

struct FlippedShape<T: Shape>: Shape {
  var shape: T
  
  func draw() -> String {
    let lines = shape.draw().split(separator: "\n")
    return lines.reversed().joined(separator: "\n")
  }
}

private func flippedTriangle() {
  let smallTriangle = Triangle(size: 3)
  // 利用泛型来实现垂直翻转之类的操作，翻转操作的结果会暴露我们用于构造结果的泛型类型
  let flippedTriangle = FlippedShape(shape: smallTriangle)
  print(flippedTriangle.draw())
}

struct JoinedShape<T: Shape, U: Shape>: Shape {
  var top: T
  var bottom: U
  
  func draw() -> String {
    return top.draw() + "\n" + bottom.draw()
  }
}

private func joinedShape() {
  let smallTriangle = Triangle(size: 3)
  let flippedTriangle = FlippedShape(shape: smallTriangle)
  
  let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
  print(joinedTriangles.draw())
}


struct Square: Shape {
  var size: Int
  
  func draw() -> String {
    let line = String(repeating: "*", count:  size)
    let result = Array<String>(repeating: line, count: size)
    return result.joined(separator: "\n")
  }
}

/// 梯形，公共接口的基本性质 —— 返回的是一个几何图形 —— 而不是部分的公共接口生成的特殊类型。
///
/// - Returns: 遵循 Shape 协议的给定类型
private func makeTrapezoid() -> some Shape {
  let top = Triangle(size: 3)
  let middle = Square(size: 3)
  let bottom = FlippedShape(shape: top)
  
  let trapezoid = JoinedShape(
    top: top,
    bottom: JoinedShape(top: middle, bottom: bottom)
  )
  return trapezoid
}

private func testTrapezoid() {
  let trapezoid = makeTrapezoid()
  print(trapezoid.draw())
}

func flip<T: Shape>(_ shape: T) -> some Shape {
  return FlippedShape(shape: shape)
}
func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape {
  JoinedShape(top: top, bottom: bottom)
}

private func testOpaqueJoinedTriangles() {
  let smallTriangle = Triangle(size: 3)
  
  let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
  print(opaqueJoinedTriangles.draw())
}

/**
/// 违反了返回值类型唯一的要求
func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
  if shape is Square {
    return shape // 错误：返回类型不一致
  }
  return FlippedShape(shape: shape) // 错误：返回类型不一致
}
 */

struct FlippedShape1<T: Shape>: Shape {
  var shape: T
  func draw() -> String {
    if shape is Square {
      return shape.draw()
    }
    let lines = shape.draw().split(separator: "\n")
    return lines.reversed().joined(separator: "\n")
  }
}

func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
  return FlippedShape1(shape: shape) // 错误：返回类型不一致
}

/// 返回值始终还是同样的底层类型 [T]， 所以这符合不透明返回类型始终唯一的要求。
func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
  return Array<T>(repeating: shape, count: count)
}
